home *** CD-ROM | disk | FTP | other *** search
/ Compendium Deluxe 2 / LSD and 17bit Compendium Deluxe - Volume II.iso / a / prog / asmsrc / thesource-7.lha / Source / Articles / Stereoscopic / Stereo.lha / stereo.c < prev   
C/C++ Source or Header  |  1994-05-27  |  15KB  |  426 lines

  1. /* Name : stereo.c
  2.  *
  3.  * Notes: No double buffer is done in this program, so an extra
  4.  *        WaitTOF() is needed to prevent the display from displaying
  5.  *        graphics before they are drawn, that is, it prevents the
  6.  *        top half of the screen from "disappearing." :)
  7.  *
  8.  *      $Log$
  9.  */
  10. /******************************************************************************/
  11.  
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <exec/types.h>
  15. #include <exec/memory.h>
  16.  
  17. #include <intuition/intuition.h>
  18. #include <intuition/screens.h>
  19.  
  20. #include <clib/exec_protos.h>
  21. #include <clib/intuition_protos.h>
  22. #include <clib/graphics_protos.h>
  23. #include <clib/dos_protos.h>
  24.  
  25. /******************************************************************************/
  26.  
  27. /* This is the general 8:8 fixed point data type.  With this data type, 1.0
  28.  * is represented by 0x0100.  Because of this, multiplication and division will
  29.  * introduce a ``magnitude error''.  For example, 1.0 * 1.0 = 1.0, but 0x100 *
  30.  * 0x100 = 0x10000.  To correct this, the result of multiplication must be
  31.  * shifted to the right by 8 bits, and the result of a division must be
  32.  * shifted to the left by 8 bits. */
  33. typedef WORD Fixed;
  34.  
  35.  
  36. /* This is a 2D point and is usually considered to be in device coordinates. */
  37. typedef struct
  38. {
  39.     WORD    x;
  40.     WORD    y;
  41. } Point2D;
  42.  
  43.  
  44. /* This is a 3D point and is always in world/local coordinates. */
  45. typedef struct
  46. {
  47.     Fixed   x;
  48.     Fixed   y;
  49.     Fixed   z;
  50. } Point3D;
  51.  
  52.  
  53. /* This structure models one surface (polygon).  It consists
  54.  * of a pointer to the next polygon, the number of points for
  55.  * itself, and a list of points.  This list is NOT actual points,
  56.  * but rather the indexes of the points in its objects point
  57.  * list. */
  58. typedef struct
  59. {
  60.     APTR  * Next;
  61.  
  62.     WORD    NumPoints;
  63.     WORD  * Points;
  64. } Surface;
  65.  
  66.  
  67. /* The object structure consists of two main parts.  The first
  68.  * is the list of points.  This consists of the number of points
  69.  * followed by a pointer to the array of points (local coords).
  70.  * The second part is a pointer to a linked list of surfaces.
  71.  * (See above). */
  72. typedef struct
  73. {
  74.     WORD        NumPoints;
  75.     Point3D   * Points;
  76.  
  77.     Surface   * Surfaces;
  78. } Object3D;
  79.  
  80.  
  81. /******************************************************************************/
  82.  
  83. void Rotate( Point3D * input, Point3D * World, Fixed XAngle, Fixed YAngle,
  84.              Fixed ZAngle, WORD NumPoints );
  85. void Project( Point3D * world, Point2D * view,
  86.               Fixed XCent, Fixed YCent, Fixed ZCent, WORD NumPoints );
  87. void Render( struct RastPort * rp, Object3D * obj, Point2D * points,
  88.              BOOL hsr, UBYTE Pen );
  89. BOOL IsSurfaceVisible( Surface * surf, Point2D * points );
  90.  
  91. /******************************************************************************/
  92.  
  93. /* To generate sin() and cos(), I just use a 256+64 entry look-up
  94.  * table that contains the values of sine from 0 to 360+90 degrees. */
  95. extern const Fixed SineTable[];
  96. #define DEG_90      (64)
  97.  
  98. /******************************************************************************/
  99.  
  100. /* This file contains the defs' for the objects */
  101. #include "objects.h"
  102.  
  103. /******************************************************************************/
  104.  
  105. #define LEFT_COLOR      1       /* Left eye is color01.     */
  106. #define RIGHT_COLOR     2       /* Right eye is color02.    */
  107.  
  108. struct ColorSpec   colors[] =
  109. {
  110.     { 0, 0, 0, 0 },             /* Background color.    */
  111.     { 1, 8, 0, 0 },             /* Left eye color.      */
  112.     { 2, 0, 0, 8 },             /* Right eye color.     */
  113.     { 3, 8, 0, 8 },             /* Left and right color.*/
  114.     { -1, 0, 0, 0 }             /* End of color list.   */
  115. };
  116.  
  117. /******************************************************************************/
  118. /* This is the part that makes this type of routine difficult.  The distnace  *
  119.  * between a persons eyes varys from person to person.  This is the number    *
  120.  * that works for me.  My eyes are about 328mm from the center of my nose.    */
  121.  
  122. #define EYE_DISTANCE    50      /* From your nose to the middle of your eye.  */
  123.  
  124. /******************************************************************************/
  125.  
  126. int main()
  127. {
  128.     struct Screen    * scr;         /* Our screen                           */
  129.     struct RastPort  * rp;          /* RastPort to draw in                  */
  130.  
  131.     Object3D         * Obj;         /* Ptr to our object                    */
  132.     /* These are my angles of rotation about the given axis. */
  133.     Fixed              XAngle, YAngle, ZAngle;
  134.     Point3D          * World;       /* Array of rotated world coordinates.  */
  135.     Point2D          * View;        /* Array of projected screen pts        */
  136.     WORD               frames;      /* Number of frames remaining           */
  137.     BOOL               hsr;         /* This turns hidden surfaces on/off    */
  138.  
  139.     /* Open the screen we want to work on. */
  140.     scr = OpenScreenTags( NULL, SA_Width,        320L,
  141.                                 SA_Height,       200L,
  142.                                 SA_Type,         CUSTOMSCREEN,
  143.                                 SA_Depth,        2L,
  144.                                 SA_Colors,       colors,
  145.                                 SA_DisplayID,    LORES_KEY,
  146.                                 SA_ShowTitle,    FALSE,
  147.                                 TAG_END );
  148.     if ( scr == NULL )
  149.     {
  150.         PutStr( "Could not open screen.\n" );
  151.         return( 5 );
  152.     }
  153.  
  154.  
  155.     /* Since I'm not double-buffering, I'll just use my screen's default
  156.      * RastPort to draw in. */
  157.     rp = &(scr->RastPort);
  158.  
  159.  
  160.     /* Allocate memory for the arrays of rotated and projected points. */
  161.     World = (Point3D *)AllocVec( sizeof( Point3D ) * Obj->NumPoints,
  162.                                  MEMF_ANY|MEMF_CLEAR );
  163.     View  = (Point2D *)AllocVec( sizeof( Point2D ) * Obj->NumPoints,
  164.                                  MEMF_ANY|MEMF_CLEAR );
  165.  
  166.  
  167.     /* Initialize the angles of rotation. */
  168.     XAngle = YAngle = ZAngle = 0L;
  169.  
  170.  
  171.     Obj = &Cylinder;
  172.     hsr = FALSE;
  173.  
  174.     /* For 600 frames (~10 seconds) we'll rotate and re-draw our happy
  175.      * vector object. */
  176.     for ( frames = 300 ; frames-- ; /* empty */ )
  177.     {
  178.         /* Wait until the end of the frame (i.e., the vertical blank) to
  179.          * draw the next frame. */
  180.         WaitTOF();
  181.  
  182.         /* Clear the screen. */
  183.         Move( rp, 0, 0 );
  184.         ClearScreen( rp );
  185.  
  186.  
  187.         /* Rotate the object by the new angles */
  188.         Rotate( Obj->Points, World, XAngle, YAngle, ZAngle, Obj->NumPoints );
  189.  
  190.  
  191.         /* Project for the left eye. */
  192.         Project( World, View, EYE_DISTANCE, 0, 0, Obj->NumPoints );
  193.         Render( rp, Obj, View, hsr, LEFT_COLOR );
  194.  
  195.         /* Project for the right eye. */
  196.         Project( World, View, -EYE_DISTANCE, 0, 0, Obj->NumPoints );
  197.         Render( rp, Obj, View, hsr, RIGHT_COLOR );
  198.  
  199.         /* See the notes above as to why this is here. */
  200.         WaitTOF();
  201.  
  202.         /* Get the new angles and make sure they are in range. */
  203.         XAngle += 1;    YAngle += 2;    ZAngle += 3;
  204.         XAngle &= 0xff; YAngle &= 0xff; ZAngle &= 0xff; 
  205.     }
  206.  
  207.  
  208.     FreeVec( World ); FreeVec( View );
  209.     CloseScreen( scr );
  210.     return( 0 );
  211. }
  212.  
  213. /******************************************************************************/
  214. /* Name : Rotate()
  215.  *
  216.  * Notes: Transforms the given array of points by the specified angles.
  217.  */
  218.  
  219. void Rotate( Point3D * input, Point3D * World, Fixed XAngle, Fixed YAngle,
  220.              Fixed ZAngle, WORD NumPoints )
  221. {
  222.     Fixed       matrix[3][3];        /* transform matrix    */
  223.     /* These are my cos and sin values for the three angles     */
  224.     Fixed       sinx, cosx, siny, cosy, sinz, cosz;
  225.     Fixed       temp;                   /* temp storage for sub exprs */
  226.  
  227.  
  228.     /* Get all the sine and cosine values. */
  229.     sinx = SineTable[ XAngle ]; cosx = SineTable[ XAngle + DEG_90 ];
  230.     siny = SineTable[ YAngle ]; cosy = SineTable[ YAngle + DEG_90 ];
  231.     sinz = SineTable[ ZAngle ]; cosz = SineTable[ ZAngle + DEG_90 ];
  232.  
  233.     /* My rotation matrix looks like this:
  234.      *
  235.   cos(z)cos(y)  sin(z)cos(x)+cos(z)sin(y)sin(x)  sin(z)sin(x)-cos(z)sin(y)cos(x)
  236.  -sin(z)cos(y)  cos(z)cos(x)-sin(z)sin(y)sin(x)  cos(z)sin(x)+sin(z)sin(y)cos(x)
  237.   sin(y)        -cos(y)sin(x)                    cos(y)sin(x)
  238.      *
  239.      * I chose this matrix because it is easy to code and still provides a
  240.      * reasonable modeling transform for this situation.
  241.      */
  242.  
  243.  
  244.     /* Calculate column 1. */
  245.     matrix[0][0] = ( cosz * cosy) >> 8;
  246.     matrix[1][0] = (-sinz * cosy) >> 8;
  247.     matrix[2][0] = siny;
  248.  
  249.     /* Calculate column 2. */
  250.     temp = ( sinx * siny ) >> 8;
  251.     matrix[0][1] = (sinz*cosx + cosz*temp) >> 8;
  252.     matrix[1][1] = (cosz*cosx - sinz*temp) >> 8;
  253.     matrix[2][1] = (-cosy*sinx) >> 8;
  254.  
  255.     /* Calculate column 3. */
  256.     temp = (siny*cosx) >> 8;
  257.     matrix[0][2] = (sinz*sinx - cosz*temp) >> 8;
  258.     matrix[1][2] = (cosz*sinx + sinz*temp) >> 8;
  259.     matrix[2][2] = (cosy*cosx) >> 8;
  260.  
  261.  
  262.     /* Send each input point through the transformation
  263.      * matrix and store the result. */
  264.     for( /* empty */ ; NumPoints-- ; input++, World++ )
  265.     {
  266.         World->x = ( matrix[0][0] * input->x +
  267.                      matrix[0][1] * input->y +
  268.                      matrix[0][2] * input->z ) >> 8;
  269.         World->y = ( matrix[1][0] * input->x +
  270.                      matrix[1][1] * input->y +
  271.                      matrix[1][2] * input->z ) >> 8;
  272.         World->z = ( matrix[2][0] * input->x +
  273.                      matrix[2][1] * input->y +
  274.                      matrix[2][2] * input->z ) >> 8;
  275.     }
  276.  
  277.     return;
  278. }
  279.  
  280. /******************************************************************************/
  281. /* Name : Project()
  282.  *
  283.  * Notes: This function projects an array of 3D points to a 2D array using
  284.  *        either perspective or parallel projection.
  285.  */
  286.  
  287. void Project( Point3D * world, Point2D * view,
  288.               Fixed XCent, Fixed YCent, Fixed ZCent,
  289.               WORD NumPoints )
  290. {
  291.     while( NumPoints-- )
  292.     {
  293.         Fixed       base;       /* project by dividing by this. */
  294.  
  295.  
  296.         /* First we need to decide if the Z value of the point should
  297.          * have any bearing on the projection.  A base of 1500 is used
  298.          * for two reasons:
  299.          *      1. Prevents divide by 0 and divide by negative numbers
  300.          *      2. Minimizes the difference between parallel and
  301.          *         perspective projection.
  302.          *
  303.          * Then the object's Z value is added.  This won't effect the
  304.          * projection, but will give an overall depth-cue by scaling
  305.          * the whole object. */
  306.         base = (world->z + 1500) + ZCent;
  307.  
  308.         /* Project the point... */
  309.         view->x = ((LONG)(world->x + XCent) << 8) / base;
  310.         view->y = ((LONG)(world->y + YCent) << 8) / base;
  311.  
  312.         /* ...and transform to screen coordinates. */
  313.         view->x += 160;
  314.         view->y += 100;
  315.  
  316.         view++;
  317.         world++;
  318.     }
  319. }
  320.  
  321. /******************************************************************************/
  322. /* Name : Render()
  323.  *
  324.  * Notes: This function renders the given 3D object, using the specified
  325.  *        2D device coordinates to the given RastPort.
  326.  */
  327.  
  328. void Render( struct RastPort * rp, Object3D * obj, Point2D * points,
  329.              BOOL hsr, UBYTE Pen )
  330. {
  331.     WORD       x,y;         /* coords of the first point this poly  */
  332.     Surface  * surface;     /* ptr to the current surface           */
  333.  
  334.  
  335.     SetAPen( rp, Pen );
  336.  
  337.     for ( surface = obj->Surfaces ; surface != NULL ; surface = surface->Next )
  338.     {
  339.         if ( !hsr || IsSurfaceVisible( surface, points ) )
  340.         {
  341.             WORD  NumPoints;    /* # of points this surface             */
  342.             WORD  lcv;          /* current point for this polygon       */
  343.  
  344.  
  345.             /* Fetch the number of points and the first point. */
  346.             NumPoints = surface->NumPoints;
  347.             x = points[ surface->Points[0] - 1 ].x;
  348.             y = points[ surface->Points[0] - 1 ].y;
  349.  
  350.  
  351.             /* Move to the first point to start drawing. */
  352.             Move( rp, x, y );
  353.  
  354.  
  355.             /* For each point in the polygon, draw a line.  Note: the
  356.              * point indexes in the polygon structure are stored as
  357.              * base 1, whereas the point array is base 0. */
  358.             for( lcv = 1 ; lcv < NumPoints ; lcv++ )
  359.             {
  360.                 Draw( rp, points[ surface->Points[lcv] - 1 ].x,
  361.                           points[ surface->Points[lcv] - 1 ].y );
  362.         }
  363.  
  364.  
  365.             /* Connect back to the first point. */
  366.             Draw( rp, x, y );
  367.         }
  368.     }
  369.  
  370.     return;
  371. }
  372.  
  373. /******************************************************************************/
  374. /* Name : IsSurfaceVisible()
  375.  *
  376.  * Notes: This function examines the give surface and determines if it
  377.  *        faces toward the viewer.  If it does, this function will return
  378.  *        true, otherwise it will return false.
  379.  *
  380.  *        The algorithm for this routine can be arrived at two different ways.
  381.  *        The way that I use goes something like this:
  382.  *
  383.  *        If three points are counter-clockwise, then the angle between the
  384.  *        two rays they define will be less than 180 degrees.  This means that
  385.  *        given the following two lines:
  386.  *
  387.  *               / A        The slope of line BC will be less than the slope
  388.  *              /           of line AB.  If DX1 is the delta X from A to B,
  389.  *             /            DY1 is the delta Y from A to B, DX2 is the delta
  390.  *            /             from B to C, and DY2 is the delta Y from B to C,
  391.  *         B <              then (DY1 / DX1) > (DY2 / DX2) must be true for a
  392.  *            \             counter clockwise surface.  Cross multiplication
  393.  *             \            simplifies this to (DX2 * DY1) > (DX1 * DY2).
  394.  *              \ C
  395.  *
  396.  *        This same formula can be derived by reducing the cross product formula
  397.  *        to only look at the Z values, but I'll leave that as an exercise for
  398.  *        the reader. ;^)
  399.  */
  400.  
  401. BOOL IsSurfaceVisible( Surface * surf, Point2D * points )
  402. {
  403.     Fixed   x1, y1, x2, y2, x3, y3; /* three points on the surface  */
  404.     Fixed   dx1, dx2, dy1, dy2;     /* delta values                 */
  405.  
  406.  
  407.     /* Fetch the first three points of the surface. */
  408.     x1 = points[ surf->Points[0] - 1 ].x;
  409.     y1 = points[ surf->Points[0] - 1 ].y;
  410.     x2 = points[ surf->Points[1] - 1 ].x;
  411.     y2 = points[ surf->Points[1] - 1 ].y;
  412.     x3 = points[ surf->Points[2] - 1 ].x;
  413.     y3 = points[ surf->Points[2] - 1 ].y;
  414.  
  415.  
  416.     /* Calculate the deltas. */
  417.     dx1 = x1 - x2;
  418.     dx2 = x2 - x3;
  419.     dy1 = y1 - y2;
  420.     dy2 = y2 - y3;
  421.  
  422.  
  423.     /* Do the magic! */
  424.     return( (dx1 * dy2) < (dx2 * dy1) );
  425. }
  426.